fix(pane-stream): release FIFO read fd on pane WebSocket disconnect - #116
Merged
Conversation
PaneStreamer.run() transfers the FIFO read fd into an asyncio read transport via connect_read_pipe and sets the bare `fd` to -1, but the finally block only guarded that now-dead `fd` and never closed the transport. The transport only self-closes on EOF — and on a WS disconnect the tmux `cat` writer is still alive, so no EOF arrives and the read fd leaks one-per-stream (the .fifo is unlinked but the fd stays open). Over a long session of opening pane terminals this marches toward the process fd limit and bloats the asyncio selector, slowing every backend operation including the keystroke round-trip. Capture the transport and close it in finally (with file_obj / bare-fd fallbacks for the earlier failure windows). transport.close() releases the fd and deregisters the loop reader. Verified live: was +1 FIFO fd per terminal open; now 0 leaked across clean and worst-case mid-stream open/close cycles. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a backend file-descriptor leak that degrades responsiveness the longer Switchboard stays open — the UI gets sluggish and typing into panes lags.
Reported symptom: "As the application stays open over time, the UI becomes much slower, typing isn't as responsive."
Root cause
PaneStreamer.run()streams a pane by transferring the FIFO read fd into an asyncio read transport viaconnect_read_pipe, then sets the barefd = -1. Thefinallyblock only guarded that now-deadfdand never closed the transport:The transport only self-closes on EOF. On a WebSocket disconnect the tmux
catwriter is still alive, so no EOF arrives — the.fifois unlinked but the read fd leaks, one per terminal open. Over a working session this climbs toward the process fd soft limit (256 on macOS) and bloats the asyncio selector (it polls every dead FIFO fd each loop iteration), slowing every backend operation — including the keystroke round-trip that carries typing.Fix
Capture the transport from
connect_read_pipeand close it deterministically infinally, withfile_obj/ bare-fdfallbacks for the earlier failure windows.transport.close()releases the fd and deregisters the loop reader.Verification
test_run_releases_fifo_read_fd_on_ws_disconnect— models the exact no-EOF disconnect path (writer held open) and asserts no fd survives teardown. Fails before the fix (leaked 1 fd), passes after.Notes
🤖 Generated with Claude Code